/*
 Copyright (c) 2008 TPSi
 Alex Iskander
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:
 
 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 
 */

#include "Mem.h"
#include <stdlib.h>

#ifndef BINDABLE_REF_H_
#define BINDABLE_REF_H_
namespace Bind
{
	/**
	 \class Ref
	 \brief Basic type for any reference.
	 */
	class Ref
	{
	public:
		/**
		 \fn Ref();
		 \brief Constructor.
		 */
		Ref()
		{
			this->ptr = NULL;
		}
		
		/**
		 \fn get();
		 \brief Returns the Mem object pointed to by the ptr.
		 */
		Mem *get()
		{
			return this->ptr;
		}
		
		/**
		 \fn operator==(Ref &ref);
		 \brief Compares to another reference.
		 */
		bool operator==(Ref &ref)
		{
			return this->ptr == ref.ptr;
		}
		
		/**
		 \fn operator<(Ref &ref);
		 \brief Compares to another reference.
		 */
		bool operator<(Ref &ref)
		{
			return (this->ptr < ref.ptr);
		}
		
		/**
		 \fn operator>(Ref &ref);
		 \brief Compares to another reference.
		 */
		bool operator>(Ref &ref)
		{
			return (this->ptr > ref.ptr);
		}
		
		/**
		 \fn ~Ref();
		 \brief Destructor.
		 */
		virtual ~Ref()
		{
			this->ptr = NULL;
		}
		
	protected:
		Mem *ptr;
	};
	
	/**
	 \class R
	 \brief A smart pointer.
	 
	 Since it will, theoretically, be used quite often, it seems best to keep
	 the typename as short as possible. You can't get much shorter than one
	 letter. So, for a pointer to a BindableObject, you will no longer use
	 Bindable *; instead, you will use R<BindableObject>. In total, it is two more
	 characters: one for the >, and the other for the space which will have
	 to follow for code clarity. Not bad.
	 */
	template <typename Type>
	class R : public Ref
	{
	public:
		/**
		 \fn R();
		 \brief Creates a new reference with nothing.
		 */
		R()
		{
			this->value = NULL;
			this->ptr = NULL;
		}
		
		/**
		 \fn R(Type *value);
		 \brief Creates a new reference referencing the specified pointer.
		 */
		R(Type *value)
		{
			this->set(value);
		}
		
		
		/**
		 \fn R(Ref &ref);
		 */
		R(R<Type> const &ref)
		{
			this->set(*ref);
		}
		
		
		Type *operator*() const
		{
			return this->value;
		}
		
		Type *operator->()
		{
			return this->value;
		}
		
		operator Type *()
		{
			return this->value;
		}
		
		bool operator !()
		{
			if (this->value == NULL)
				return true;
			return false;
		}
		
		operator bool ()
		{
			if (this->value == NULL)
				return false;
			return true;
		}

		using Ref::operator==;
		using Ref::operator>;
		using Ref::operator<;
		
		/**
		 \fn isEmpty();
		 \brief Returns true if the pointer is NULL.
		 */
		bool isEmpty()
		{
			if (this->value == NULL)
				return true;
		}
		
		/**
		 \fn operator=(R<Type> &ref);
		 \brief Assigns the object pointed to by ref to the pointer.
		 */
		R<Type> &operator=(R<Type> &ref)
		{
			//if there is a pointer, deref it
			this->unset();
			
			//set
			this->set(*ref);
			
			//return self
			return *this;
		}
		
		template <typename From>
		R<Type> &operator=(R<From> &from)
		{
			this->unset();
			this->set(*from); //if it is a compatible class, this will work.
		}
		
		/**
		 \fn operator=(Ref &ref);
		 \brief Assigns the value pointed to by Ref, assuming it can be
		 dynamically cast to the pointer's type, to the pointer.
		 */
		R<Type> &operator=(Ref &ref)
		{
			Type *object = dynamic_cast<Type *> (ref.get());
			this->set(object);
		}
		
		/**
		 \fn operator=(Type *value);
		 \brief Assigns the value to the pointer.
		 */
		R<Type> &operator=(Type *value)
		{
			this->unset();
			this->set(value);

			return *this;
		}
		
		/**
		 \fn set(Type *value);
		 \brief Sets the pointer to point to value.
		 */
		void set(Type *value)
		{
			this->unset();
			this->value = value;
			this->ptr = value;
			
			//increase ref count if value is not null
			if (value != NULL)
				value->ref();
		}
		
		void unset()
		{
			//return if there is no pointer
			if (!this->ptr) 
				return;
			
			//check reference count
			int count = this->ptr->deref();
			
			//if count is less than one
			if (count < 1)
				//delete the pointer
				delete this->value;
			
			//set ptr to NULL
			this->ptr = NULL;
			
			//set value to NULL
			this->value = NULL;
		}
		
		/**
		 \fn close();
		 \brief Dereferences the pointer without deleting it.
		 */
		Type *close()
		{
			//do nothing if there is no pointer
			if (!this->ptr)
				return NULL;
			
			//deref
			this->ptr->deref();
			
			//store
			Type *result = this->value;
			
			//set ptr and value to null
			this->ptr = NULL;
			this->value = NULL;
			
			//return
			return result;
		}
		
		/**
		 \fn ~R();
		 \brief Destructor. Dereferences and deletes pointer if needed.
		 */
		~R()
		{
			//unset
			this->unset();
		}
	protected:
		Type *value;
	};
	
}

#endif